package com.myapi.ModelPoker;
import java.util.*;

import com.myapi.client.PlayerHandDAO;
import com.myapi.client.DAO.CardDAO;
import com.myapi.client.DAO.TableDAO;
import com.myapi.client.DAO.TablePotDAO;





public class Table {
	
	public static int MAX_PLAYERS = 4;  
	private List<PlayerHand> playerHands;
	private String id;  //unique ID for table, to allow for multiple tables at once (tournament style)
	private int buttonPosition;  //the position (in Player list) of the dealer button. Small blind = + 1, Big blind = + 2
	private List<TablePot> pots;
	private Dealer dealer;
	private double currentBet;
	ArrayList<String> Forum;
	private ArrayList<Card> commCards;
	boolean gameRunning;

	/**
	 * Constructor Table
	 * @param id unique ID for table, to allow for multiple tables at once (tournament style)
	 */
	public Table(String id) {
		setId(id);
		buttonPosition = 0;
		playerHands = new ArrayList<PlayerHand>(MAX_PLAYERS);
		pots = new ArrayList<TablePot>();
		Forum= new ArrayList<String>();
		commCards=new ArrayList<Card>();

	}

	/**
	 * Add Pot Contribution in the table
	 * @param pC PotContribution added
	 * @param notify Element for the Dealer who observe the pot
	 */
	public void addPotContribution(PotContribution pC, boolean notify) {
		setCurrentBet(pC);
		boolean createNew = false;  //true if we need to create a new pot for the contribution
		if(pots.size() == 0)
			createNew = true;
		else if(getActivePot().getClosed())  //if the last pot is closed (only the last pot should ever be open)
			createNew = true;
		if(createNew) {
			//create new TablePot
			TablePot tP = new TablePot();
			tP.addObserver(getDealer());
			tP.addContribution(pC, notify);                
			pots.add(tP);                
		} else {
			TablePot tP = getActivePot();
			tP.addContribution(pC, notify);                
		}

	}

	/**
	 * TODO: currently only deals with one all in, cannot create multiple side pots at once
	 * to be called by Dealer when a betting round is complete
	 * @param tP TablePot
	 * @return true if we create the sidePote False if all the player haven't raised yet
	 */


	//TODO: currently only deals with one all in, cannot create multiple side pots at once
	//to be called by Dealer when a betting round is complete
	public boolean createSidePot(TablePot tP) {
		List<PotContribution> conts = tP.getContributions();
		
		if(conts.size()==1)
			return true;
		List<PotContribution> allInConts=new  ArrayList<PotContribution>();
		for(int i = 0; i < conts.size(); i++) {
			PotContribution pC = conts.get(i);
			if(pC.getAllIn()) {
				allInConts.add(pC);
			}
		}
		double allInMinAmount=0;
		if(allInConts.size()!=0)
			allInMinAmount = allInConts.get(0).getAmount();
		else{               // if we have create all the side pot we need to close the last pot (no player all in)
			tP.setContributionsComplete();
			return true;
		}
		for(int i = 1; i < allInConts.size(); i++) {
			if(allInMinAmount >allInConts.get(i).getAmount()) {
				allInMinAmount = allInConts.get(i).getAmount();
			}
		}

		if(allInMinAmount >= getCurrentBet())
			return false;  //don't create a new side pot if the all in hasn't been raised;
			TablePot sidePot = new TablePot();
			sidePot.addObserver(getDealer());
			for(int j = 0; j < conts.size(); j++) {
				PotContribution pC = conts.get(j);
				if(pC.getAmount() > allInMinAmount) {
					PotContribution newCont = new PotContribution(pC.getPlayer(), pC.getAmount() - allInMinAmount, pC.getAllIn(), false);
					pC.setAmount(allInMinAmount);
					sidePot.addContribution(newCont, false);
				}
			}        
			pots.add(sidePot);
			tP.setClosed(true);
			return createSidePot(sidePot);

	}

	/**
	 * the active pot is always the last to be added
	 * @return TablePot
	 */
	public TablePot getActivePot() {
		if(pots.size() > 0)
			return pots.get(pots.size() - 1);  //the active pot is always the last to be added
		else
			return null;            
	}

	/**
	 * Called when a player folds, this mucks all contributions in all pots from that player
	 * @param player Player
	 */
	public void muckAllContributions(Player player) {
		List<TablePot> tPots = getPots();

		TablePot tablePot;
		for(int i = 0; i < tPots.size(); i++) {
			tablePot = tPots.get(i);
			tablePot.muckContributions(player);
		}
	}

	public void clearPots() {
		pots.clear();
	}


	/**
	 * TODO: specify where to add playerHand? ie if a player leaves, and we want to insert into the gap
	 * add a playerHand to the table only if a hand for that player doesn't already exist at the table
	 * @param playerHand  PlayerHand
	 */
	public void addPlayerHand(PlayerHand playerHand) {
		if(playerHands.size() <= MAX_PLAYERS) {  //check if table full
			if(playerHands.indexOf(playerHand) == -1) {
				playerHands.add(playerHand);
			}
			else {
				System.out.println("Could not add playerHand for player ID: " + playerHand.getPlayer().getId() + ", playerHand already exists at this table for that player!");
			}
		}
		else {
			System.out.println("Table full, cannot add playerHand for player ID: " + playerHand.getPlayer().getId());
		}
	}

	/**
	 * Remove a given playerHand from table if a PlayerHand for the same Player exists at table
	 * @param playerHand
	 */
	public void removePlayerHand(PlayerHand playerHand) {
		if(playerHands.indexOf(playerHand) != -1) {
			playerHands.remove(playerHand);
		}
		else {
			System.out.println("Could not remove player hand for player ID: " + playerHand.getPlayer().getId() + ", playerHand not found for that player!");
		}
	}

	/**
	 * Move the button to the next player (for the next hand)
	 */
	public void incrementButton() {
		buttonPosition = (buttonPosition + 1) % MAX_PLAYERS;
	}

	public String getId() {
		return id;
	}

	public double getCurrentBet() {
		return currentBet;
	}

	public void setCurrentBet(double bet) {
		currentBet = bet;
	}

	/**
	 * Set the player's bet into the Pot
	 * @param pC PotContribution is the same word for bet
	 */
	private void setCurrentBet(PotContribution pC) {
		double currentBet = getCurrentBet();
		double amount = pC.getAmount();
		if(amount > getCurrentBet())
			currentBet = pC.getAmount();  //to avoid setting the current bet lower than it should be when there is a smaller all in or call

		TablePot activePot = getActivePot();

		//Add the new bet and previous bet  to current bet
		if(activePot != null) {
			PotContribution existingPc = activePot.getPlayerContribution(pC.getPlayer());
			if(existingPc != null && (pC.getAmount() + existingPc.getAmount())>getCurrentBet())
				currentBet = pC.getAmount() + existingPc.getAmount();  
		}
		setCurrentBet(currentBet);
	}

	public int getButtonPosition() {
		return buttonPosition;
	}

	/**
	 * 
	 * @return Number of player who play again in the table
	 */
	public int getNumPlayerHands() {
		return getPlayerHands().size();
	}

	/**
	 * 
	 * @return return the number of playerHands that aren't mucked or allin
	 */
	public int getNumActivePlayerHands() {
		List<PlayerHand> pHs = getPlayerHands();
		int count = 0;
		for(int i = 0; i < pHs.size(); i++) {
			if(!pHs.get(i).getMucked()&&!pHs.get(i).isAllIn())
				count++;
		}
		return count;
	}

	public List<PlayerHand> getPlayerHands() {
		return playerHands;
	}

	/**
	 * add a card to the hand at that position relative to button, button = 0
	 * @param card Card
	 * @param handPos Position of the Player
	 */
	public void addCardToHand(Card card, int handPos) {
		if(handPos <= getNumPlayerHands()) {  //only add if valid range
			PlayerHand pH = getPlayerHand(handPos);
			pH.addCard(card);
		}
	}

	/**
	 * PlayerHand at that position relative to button button = 0
	 * @param pos Position of the Player 
	 * @return PlayerHand at that position relative to button button = 0
	 */
	public PlayerHand getPlayerHand(int pos) {
		int truePos = getTruePos(pos); 
		return playerHands.get(truePos);
	}
	/**
	 *    
	 * @param pos Position with taking in considaration the button (1 for the player just after the dealer)
	 * @return Position in the dealer's view
	 */
	private int getTruePos(int pos) {
		return (pos + getButtonPosition()) % MAX_PLAYERS;
	}

	public List<Player> getPlayers() {
		List<PlayerHand> hands = getPlayerHands();
		List<Player> players = new ArrayList<Player>();
		for(int i = 0; i < hands.size(); i++) {
			PlayerHand playerH = hands.get(i);
			players.add(playerH.getPlayer());
		}
		return players;
	}

	//	public void printPlayerHands() {
		//		for(int i = 0; i < playerHands.size(); i++) {
	//			PlayerHand pH = playerHands.get(i);
	//			Player player = pH.getPlayer();
	//			System.out.print(player.getName() + ": ");
	//			Set<Card> cards = pH.getCards();
	//			Iterator<Card> iter = cards.iterator();
	//			while(iter.hasNext()) {
	//				Card card = iter.next();
	//				System.out.print(card.getValue() + card.getSuit() + " ");				
	//			}
	//			System.out.println();
	//		}
	//		System.out.println();
	//	}

	public Dealer getDealer() {
		return dealer;
	}

	public void setDealer(Dealer dealer) {
		this.dealer = dealer;
	}

	public List<TablePot> getPots() {
		return pots;
	}

	private void setId(String id) {
		this.id = id;
	}
	public void addMessageForum(String message){
		this.Forum.add(message);
	}

	public TableDAO getInfo() {
		TableDAO tableDAO=new TableDAO();
		tableDAO.setButtonPosition(this.getButtonPosition());
		tableDAO.setCurrentBet(this.getCurrentBet());
		tableDAO.setDealer(this.getDealer().getInfo());
		tableDAO.setId(this.getId());
		tableDAO.setForum(this.getForum());
		ArrayList<PlayerHandDAO> playerInfo=new ArrayList<PlayerHandDAO>();
		for(int i=0;i<this.getPlayerHands().size();i++){
			playerInfo.add(this.getPlayerHands().get(i).getInfo());
		}
		tableDAO.setPlayerHands(playerInfo);

		ArrayList<TablePotDAO> tablePotDAO=new ArrayList<TablePotDAO>();
		for(int i=0;i<this.getPots().size();i++){
			tablePotDAO.add(this.getPots().get(i).getInfo());
		}
		tableDAO.setPots(tablePotDAO);

		ArrayList<CardDAO> commCard=new ArrayList<CardDAO>();
		for(int i=0;i<this.commCards.size();i++){
			commCard.add(this.commCards.get(i).getInfo());
		}
		tableDAO.setCommCards(commCard);
		tableDAO.setGameRunning(this.gameRunning);
		return tableDAO;
	}

	public void setInfo(TableDAO tableDAO) {
		// TODO Auto-generated method stub

	}

	public ArrayList<String> getForum() {
		return Forum;
	}

	public void setForum(ArrayList<String> forum) {
		Forum = forum;
	}

	public void addCommCard(Card card) {
		commCards.add(card);

	}
}
